from django.http import HttpResponse, Http404, HttpResponseRedirect
from django.template import TemplateDoesNotExist, RequestContext
from django.shortcuts import render_to_response
from django import forms
from settings import MEDIA_ROOT
from ktool.models import *
import sys
sys.path.append('/usr/lib/python-support/python-glpk/python2.6')
import glpk

class fbaform(forms.Form):
    file1  = forms.FileField() #the rea list
    file2 = forms.FileField(required=False) #the predefined external metabolite list
    file3 = forms.FileField(required=False) #the metabolites should always be internal even can not be balanced
    trmet = forms.CharField(label="Input metabolites", required=True)
    trmetlb=forms.CharField(label="low boundary for the input flux", required=False)
    trmetub=forms.CharField(label="up boundary for the input flux", required=False)
    objc=forms.CharField(label="objective compound whose flux to be maximinized", required=True)
    def complet(self): #Verifiy that all information has been filled
        if self.cleaned_data['file1'] or self.cleaned_data['trmet'] or self.cleaned_data['objc']:
           raise forms.ValidationError(_(u'You must give all the information required'))
        return self.cleaned_data

def fba(request):
    if request.method == 'POST':
        f = fbaform(request.POST,request.FILES)
        if not f.is_valid():
           err="sth wrong"
           return render_to_response('fbastart.html', locals(),context_instance=RequestContext(request)) #do sth else 
        else:
            d={} #compound dictionary to save the participated reactions and coefficients
            comps=[]
            exmet={} #list possible external metabolites, can also read predefined ex mets from a file
            fwm=open(MEDIA_ROOT+'/metnet.mod','w')
            if f.cleaned_data["file2"]:
                exmetf=request.FILES['file2']
#                exmet1=[a.strip('\n') for a in exmetf.readlines()]
                exmet1=[a.strip('\r\n') for a in exmetf.readlines()] #linux version of new line
            else:
                exmet1=[]  
#            a=b    
            if f.cleaned_data["trmet"]:
                met=f.cleaned_data["trmet"]
                if f.cleaned_data["trmetlb"]:
                    lb=f.cleaned_data["trmetlb"]
                else:
                    lb=-10    
                if f.cleaned_data["trmetub"]:
                    ub=f.cleaned_data["trmetub"]
                else:
                    ub=10 
                trmets=[(met, float(lb), float(ub))]
#                a=b
                for tr in trmets:
                    fwm.write('var r'+tr[0]+';'+'\n')
                    exmet[tr[0]]='F' #then that compound must not be external
                    fwm.write('s.t. cr'+tr[0]+'1: r'+tr[0]+' >='+str(tr[1])+';\n')
                    fwm.write('s.t. cr'+tr[0]+'2: r'+tr[0]+' <='+str(tr[2])+';\n')
                    d[tr[0]]=[('r'+tr[0], 1.0)] #by default the metabolite is on the right side as product (input), add it to the stoi matrix (represented as a dictionary)
                    comps.append(tr[0])
            if f.cleaned_data["objc"]:
                objc=f.cleaned_data["objc"]
                objR='r'+objc
                d[objc]=[(objR, -1.0)] #in the added tranport rea, objc on left side, therefore maximize production rate
                fwm.write('var '+objR+';'+'\n')
                fwm.write('maximize obj: '+objR+';\n')
                exmet[objc]='F' #must not be external as new transport rea introduced
                comps.append(objc)
            if f.cleaned_data["file1"]:
                metnet=request.FILES['file1']
                for s in metnet.readlines():
                    r=s.strip('\n').split('\t')
                    fwm.write('var '+r[0]+';'+'\n')
                    if  r[1]=='F': #only bound for irreversible reas
                        fwm.write('s.t. c'+r[0]+': '+r[0]+' >=0;\n')
                    eq=r[3]
                    if not 'n' in eq: #not for reactions with coefficients n, n+1, etc
                       a=eq.split('=')
                       if not a[0].strip()=='':
                        subs=a[0].strip().split('+')
                        for sub in subs:
                            sub=sub.strip()
                            b=sub.split(' ') #coefficient and compound ID seperated by space
                            if len(b)==1:
                                sto=-1.0
                                species=str(b[0])
                            else:
                                try:
                                   sto=-float(b[0])
                                except ValueError:
                                   sto=-1.0
                                species=str(b[1])
                            if species not in exmet1:    
#                                a=b
                                if d.has_key(species):
                                    d[species].append((r[0],sto) )
                                    if exmet[species]=='p' or exmet[species]=='sp' or r[1]=='T' :
                                         exmet[species]="F"
                                else:
                                    d[species]=[(r[0],sto) ]
                                    comps.append(species)
                                    if not exmet.has_key(species): #for notrmets already assigned as internal
                                        if r[1]=='T':
                                            exmet[species]="sp" #still possible to be external if only appear in one rea
                                        else:
                                            exmet[species]='s'
                #          fw.write(r[0]+'\t'+species+'\t'+str(sto)+'\n') 
                       if not a[1].strip()=='' : 
                        pros=a[1].strip().split('+')
                        for pro in pros:
                            pro=pro.strip()
                            b=pro.split(' ')
                            if len(b)==1:
                                sto=1.0
                                species=str(b[0])
                            else:
                                try:
                                   sto=-float(b[0])
                                except ValueError:
                                   sto=1.0
                                sto=float(b[0])
                                species=str(b[1])
                            if species not in exmet1:    
                                if d.has_key(species):
                                    d[species].append((r[0],sto) )
                                    if exmet[species]=='s' or exmet[species]=='sp' or r[1]=='T':
                                         exmet[species]="F"
                                else:
                                    d[species]=[(r[0],sto) ]
                                    comps.append(species)
                                    if not exmet.has_key(species): #for notrmets already assigned as internal
                                        if r[1]=='T':
                                            exmet[species]="sp" #still possible to be external if only appear in one rea
                                        else:
                                            exmet[species]='p'
                for comp  in comps:
                        if not exmet[comp]=='F': #add transport rea (as product) for those can not be balanced in the MN and assign boundary -1000, 1000
                            fwm.write('var r'+comp+';'+'\n') #add transport rea variables
                            fwm.write('s.t. cr'+comp+'1: r'+comp+' >=-10.0;\n') #add boundaries
                            fwm.write('s.t. cr'+comp+'2: r'+comp+' <=10.0;\n')
                            d[comp].append(('r'+comp, 1.0))  #in the transport rea, the compound as a product, update d [comp], the stoi matrix
                        s=' + '.join('%s * %s' % (r[1], r[0]) for r in d[comp])
                        fwm.write('s.t. '+comp+': '+s+' = 0;\n')
                fwm.close()
                lp=glpk.LPX(gmp=MEDIA_ROOT+'/metnet.mod')
                lp.simplex()           # Solve this LP with the simplex method
                sobj='Robj  = %g;' % lp.obj.value,  # Retrieve and print obj func value
                srea='; '.join('%s = %g' % (c.name, c.primal) for c in lp.cols if not c.primal==0) 
#                reas, rfluxes=[c.name, c.primal for c in lp.cols if not c.primal==0 and not c.name[0]=='r']
                reas=[]
                for c in lp.cols:
                    if not c.primal==0 and not c.name[0]=='r':
                        reas.append(c.name)
                return render_to_response('fbares.html', locals())
            else:
                err="please choose the network file!"
                return render_to_response('fbastart.html', locals(),context_instance=RequestContext(request))
    else:
        f = fbaform(request.POST)
        err="please choose the network file!"
        return render_to_response('fbastart.html', locals(),context_instance=RequestContext(request))



